package com.stp.yupao.utils;

import java.util.*;

/**
 * 基于标签权重的改进推荐算法
 */
public class WeightedTagUserRecommender {
    private Map<Long, Map<String, Double>> userTagMatrix;
    private Map<String, Double> tagGlobalWeights; // 标签全局权重

    public WeightedTagUserRecommender() {
        this.userTagMatrix = new HashMap<>();
        this.tagGlobalWeights = new HashMap<>();
    }

    // 添加用户标签并计算全局权重
    public void addUserTag(Long userId, String tag, double weight) {
        userTagMatrix.computeIfAbsent(userId, k -> new HashMap<>()).put(tag, weight);
        tagGlobalWeights.put(tag, tagGlobalWeights.getOrDefault(tag, 0.0) + weight);
    }

    // 计算加权相似度
    private double weightedSimilarity(Map<String, Double> user1Tags, Map<String, Double> user2Tags) {
        double score = 0.0;
        double totalWeight = 0.0;

        for (String tag : user1Tags.keySet()) {
            if (user2Tags.containsKey(tag)) {
                double globalWeight = tagGlobalWeights.getOrDefault(tag, 1.0);
                double weight = 1.0 / Math.log(1 + globalWeight); // 逆向权重，稀有标签权重更高
                score += (user1Tags.get(tag) * user2Tags.get(tag)) * weight;
                totalWeight += weight;
            }
        }

        return totalWeight > 0 ? score / totalWeight : 0.0;
    }

    public List<Long> recommendUsers(Long targetUserId, Integer topN, Set<Integer> excludedUsers) {
        if (!userTagMatrix.containsKey(targetUserId)) {
            return Collections.emptyList();
        }

        Map<String, Double> targetUserTags = userTagMatrix.get(targetUserId);
        PriorityQueue<UserScore> pq = new PriorityQueue<>(topN, Comparator.comparingDouble(UserScore::getScore));

        for (Long userId : userTagMatrix.keySet()) {
            // 跳过目标用户自己和排除列表中的用户
            if (userId == targetUserId || excludedUsers.contains(userId)) {
                continue;
            }

            Map<String, Double> candidateTags = userTagMatrix.get(userId);
            double similarity = weightedSimilarity(targetUserTags, candidateTags);

            if (pq.size() < topN) {
                pq.offer(new UserScore(userId, similarity));
            } else if (similarity > pq.peek().getScore()) {
                pq.poll();
                pq.offer(new UserScore(userId, similarity));
            }
        }

        List<Long> recommendations = new ArrayList<>(topN);
        while (!pq.isEmpty()) {
            recommendations.add(0, pq.poll().getUserId());
        }

        return recommendations;
    }
}
